summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@nokia.com>2010-05-03 12:34:27 +0200
committerTor Arne Vestbø <tor.arne.vestbo@nokia.com>2010-05-03 12:34:27 +0200
commit415b44ffb6ffcdeb4ad5b7cdd6d9d9e17ab3e47f (patch)
treed9417f99ef9665167668fe3047416a0d11e1518f
Initial checkinHEADmaster
-rw-r--r--config.h30
-rw-r--r--doxygen2qthelp.cpp407
-rw-r--r--doxygen2qthelp.pro75
-rw-r--r--examples/cpptest.ini5
-rw-r--r--examples/libspiff.ini5
-rw-r--r--examples/make_docs_cpptest.sh4
-rw-r--r--examples/make_docs_helper.sh58
-rw-r--r--examples/make_docs_libspiff.sh4
-rw-r--r--examples/make_docs_qwt.sh4
-rw-r--r--examples/qwt.ini5
-rw-r--r--htmlhelpdatainterface.cpp191
-rw-r--r--htmlhelpdatainterface_p.h86
-rw-r--r--htmlhelpparser.cpp476
-rw-r--r--htmlhelpparser_p.h81
-rw-r--r--qthelpprojectwriter.cpp172
-rw-r--r--qthelpprojectwriter_p.h59
-rw-r--r--simplehtmlparser.cpp600
-rw-r--r--simplehtmlparser_p.h60
-rw-r--r--simplehtmlreceiver_p.h49
19 files changed, 2371 insertions, 0 deletions
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..966df21
--- /dev/null
+++ b/config.h
@@ -0,0 +1,30 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define VERSION "1.1.0"
+
+#endif // CONFIG_H
+
diff --git a/doxygen2qthelp.cpp b/doxygen2qthelp.cpp
new file mode 100644
index 0000000..ae5a5be
--- /dev/null
+++ b/doxygen2qthelp.cpp
@@ -0,0 +1,407 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "config.h"
+#include "htmlhelpdatainterface_p.h"
+#include "qhelpgenerator_p.h"
+#include "qthelpprojectwriter_p.h"
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QString>
+#include <QtCore/QFile>
+#include <QtCore/QSettings>
+#include <QtCore/QUrl>
+#include <QtCore/QVariant>
+
+#if (QT_VERSION <= 0x040400)
+# error Please compile doxygen2qthelp against a Qt release _after_ 4.4.0, e.g. a recent snapshot
+#endif
+
+QT_BEGIN_NAMESPACE
+
+const char* const FILE_MISSING_FORMAT = "Error: File '%s' does not exist.\n";
+
+enum Action
+{
+ ACTION_USAGE,
+ ACTION_RUN,
+ ACTION_QUIT,
+ ACTION_VERSION
+};
+
+QString virtualFolder;
+QString nameSpace;
+QString outputFileName;
+QString inputFileName;
+bool verbose = false;
+bool forceOverwrite = false;
+bool helpProjectMode = false;
+bool error = false;
+
+void printUsage()
+{
+ printf(
+ "Usage: doxygen2qthelp OPTIONS <infile> [<outfile>]\n"
+ " doxygen2qthelp --config=<file>\n"
+ "\n"
+ "Parameters Description\n"
+ " <infile> Read input from <infile>\n"
+ " (InputFile=<infile>)\n"
+ "\n"
+ " <outfile> Write output to <outfile>\n"
+ " (OutputFile=<outfile>)\n"
+ "\n"
+ " --config=<file> Read config from <file>\n"
+ "\n"
+ " --folder=<str> Set virtual folder [required]\n"
+ " (VirtualFolder=<str>)\n"
+ "\n"
+ " --force Overwrite existing files\n"
+ " (ForceOverwrite=true)\n"
+ "\n"
+ " --help, -h Print help and exit\n"
+ "\n"
+ " --namespace=<str> Set namespace to <str> [required]\n"
+ " (Namespace=<str>)\n"
+ "\n"
+ " --qch Output as Qt Compressed Help file (*.qch) [default]\n"
+ " (QhpMode=false)\n"
+ "\n"
+ " --qhp Output as Qt Help Project (*.qhp)\n"
+ " (QhpMode=true)\n"
+ "\n"
+ " --verbose, -v Report in more detail\n"
+ " (Verbose=true)\n"
+ "\n"
+ " --version, -V Print version information and exit\n"
+ "\n"
+ );
+}
+
+void printVersion()
+{
+ printf(
+ "doxygen2qthelp " VERSION " of Qt " QT_VERSION_STR " by Trolltech ASA "
+ "(compiled " __TIME__ ", " __DATE__ ")\n"
+ );
+}
+
+void printSettings()
+{
+ printf(
+ "Settings\n"
+ " Input filename ....... %s\n"
+ " Output filename ...... %s\n"
+ "\n"
+ " QtHelpProject mode ... %s\n"
+ " Force overwrite ...... %s\n"
+ "\n"
+ " Namespace ............ %s\n"
+ " Virtual folder ....... %s\n"
+ "\n",
+ qPrintable(inputFileName),
+ qPrintable(outputFileName),
+ helpProjectMode ? "yes" : "no",
+ forceOverwrite ? "yes" : "no",
+ qPrintable(nameSpace),
+ qPrintable(virtualFolder)
+ );
+}
+
+bool checkNamespace()
+{
+ const QString uri(QLatin1String("http://") + nameSpace
+ + QLatin1String("/"));
+ const QUrl url = QUrl::fromEncoded(uri.toAscii());
+ if (!url.isValid()) {
+ printf("Error: Namespace does not conform to the rules "
+ "for the host part of a URI.\n");
+ return false;
+ }
+ return true;
+}
+
+bool checkVirtualFolder()
+{
+ const char * const VIRTUAL_FOLDER_ERROR = "Error: Virtual folder does not "
+ "conform to the rules for a path segment of a URI.\n";
+ if (virtualFolder.contains('/') || virtualFolder.contains('?')
+ || virtualFolder.contains('#')) {
+ printf(VIRTUAL_FOLDER_ERROR);
+ return false;
+ }
+
+ const QString uri(QLatin1String("http://example.org/") + virtualFolder
+ + QLatin1String("/"));
+ const QUrl url = QUrl::fromEncoded(uri.toAscii());
+ if (!url.isValid()) {
+ printf(VIRTUAL_FOLDER_ERROR);
+ return false;
+ }
+ return true;
+}
+
+bool checkFiles()
+{
+ if (inputFileName == outputFileName) {
+ printf("Error: Input and output filename identical\n");
+ return false;
+ }
+
+ if (!QFile(inputFileName).exists()) {
+ printf(FILE_MISSING_FORMAT, qPrintable(inputFileName));
+ return false;
+ }
+
+ if (!forceOverwrite && QFile(outputFileName).exists()) {
+ printf("Error: File '%s' exists. Use --force to overwrite.\n",
+ qPrintable(outputFileName));
+ return false;
+ }
+ return true;
+}
+
+void guessOutputFilename()
+{
+ if (!outputFileName.isEmpty())
+ return;
+
+ const QString newSuffix(helpProjectMode ? ".qhp" : ".qch");
+ if (inputFileName.endsWith(QLatin1String(".hhp"))) {
+ outputFileName = inputFileName.left(inputFileName.count() - 4);
+ } else {
+ outputFileName = inputFileName;
+ }
+ outputFileName += newSuffix;
+}
+
+bool run()
+{
+ guessOutputFilename();
+ Q_ASSERT(!inputFileName.isEmpty());
+ Q_ASSERT(!outputFileName.isEmpty());
+
+ if (verbose)
+ printSettings();
+
+ if (!checkNamespace() || !checkVirtualFolder() || !checkFiles())
+ return false;
+
+ HtmlHelpDataInterface helpData(inputFileName, nameSpace, virtualFolder);
+ if (helpProjectMode) {
+ // Create .qhp
+ QtHelpProjectWriter writer;
+ if (!writer.writeFile(&helpData, outputFileName)) {
+ printf("Error: Could not write to file '%s'.\n",
+ qPrintable(outputFileName));
+ return false;
+ }
+ } else {
+ // Create .qch
+ QHelpGenerator generator;
+ if (!generator.generate(&helpData, outputFileName)) {
+ printf("Error: %s\n", qPrintable(generator.error()));
+ return false;
+ }
+ }
+
+ printf("Done.\n");
+ return true;
+}
+
+void setIfSpecified(const QSettings &iniReader, const QString &key,
+ QString &output)
+{
+ const QString res = iniReader.value(key).toString();
+ if (!res.isEmpty())
+ output = res;
+}
+
+void setIfSpecified(const QSettings &iniReader, const QString &key,
+ bool &output)
+{
+ const QVariant res = iniReader.value(key);
+ if (!res.toString().isEmpty())
+ output = res.toBool();
+}
+
+bool readConfigFile(const QString &fileName)
+{
+ if (!QFile(fileName).exists()) {
+ printf(FILE_MISSING_FORMAT, qPrintable(fileName));
+ return false;
+ }
+
+ QSettings iniReader(fileName, QSettings::IniFormat);
+ setIfSpecified(iniReader, QLatin1String("ForceOverwrite"), forceOverwrite);
+ setIfSpecified(iniReader, QLatin1String("InputFile"), inputFileName);
+ setIfSpecified(iniReader, QLatin1String("Namespace"), nameSpace);
+ setIfSpecified(iniReader, QLatin1String("OutputFile"), outputFileName);
+ setIfSpecified(iniReader, QLatin1String("QhpMode"), helpProjectMode);
+ setIfSpecified(iniReader, QLatin1String("Verbose"), verbose);
+ setIfSpecified(iniReader, QLatin1String("VirtualFolder"), virtualFolder);
+ return true;
+}
+
+Action parseArgs()
+{
+ const char * const ILLEGAL_OPTION_FORMAT = "Error: Illegal option '%s'\n\n";
+ const QStringList args = QCoreApplication::arguments();
+
+ bool fileMode = false;
+ for (int i = 1; i < args.count(); i++) {
+ const QString &curArg = args.at(i);
+ if (fileMode || (curArg[0] != '-')) {
+ // Filenames only
+ switch (args.count() - i) {
+ case 2: // second last
+ inputFileName = curArg;
+ break;
+
+ case 1: // last
+ if (inputFileName.isEmpty()) {
+ inputFileName = curArg;
+ } else {
+ outputFileName = curArg;
+ }
+ break;
+
+ default:
+ if (!fileMode)
+ printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg));
+
+ error = true;
+ return ACTION_USAGE;
+ }
+ } else {
+ // Options or filenames
+ QString opt;
+ if (curArg[1] == '-') {
+ // Long option
+ opt = curArg.mid(2);
+ if (opt.isEmpty()) {
+ fileMode = true;
+ } else if (opt.startsWith(QLatin1String("config="))) {
+ if (!readConfigFile(opt.mid(6 + 1))) {
+ return ACTION_QUIT;
+ }
+ } else if (opt.startsWith("folder=")) {
+ virtualFolder = opt.mid(6 + 1);
+ } else if (opt == QLatin1String("force")) {
+ forceOverwrite = true;
+ } else if (opt == QLatin1String("help")) {
+ return ACTION_USAGE;
+ } else if (opt.startsWith("namespace=")) {
+ nameSpace = opt.mid(9 + 1);
+ } else if (opt == QLatin1String("qch")) {
+ helpProjectMode = false;
+ } else if (opt == QLatin1String("qhp")) {
+ helpProjectMode = true;
+ } else if (opt == QLatin1String("verbose")) {
+ verbose = true;
+ } else if (opt == QLatin1String("version")) {
+ return ACTION_VERSION;
+ } else {
+ printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg));
+ error = true;
+ return ACTION_USAGE;
+ }
+ } else {
+ // Short option
+ opt = curArg.mid(1);
+ if (opt.count() != 1) {
+ printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg));
+ error = true;
+ return ACTION_USAGE;
+ }
+
+ switch (opt.at(0).toAscii()) {
+ case 'h':
+ return ACTION_USAGE;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ case 'V':
+ return ACTION_VERSION;
+
+ default:
+ printf(ILLEGAL_OPTION_FORMAT, qPrintable(curArg));
+ error = true;
+ return ACTION_USAGE;
+ }
+ }
+ }
+ }
+
+ if (inputFileName.isEmpty()) {
+ printf("Error: No input filename specified\n\n");
+ error = true;
+ return ACTION_USAGE;
+ }
+
+ if (nameSpace.isEmpty()) {
+ printf("Error: Namespace must not be empty.\n\n");
+ error = true;
+ return ACTION_USAGE;
+ }
+
+ if (virtualFolder.isEmpty()) {
+ printf("Error: Virtual folder must not be empty.\n\n");
+ error = true;
+ return ACTION_USAGE;
+ }
+
+ return ACTION_RUN;
+}
+
+QT_END_NAMESPACE
+
+int main(int argc, char ** argv)
+{
+ QCoreApplication app(argc, argv);
+ const Action action = parseArgs();
+
+ switch (action) {
+ case ACTION_RUN:
+ error = ! run();
+ break;
+
+ case ACTION_VERSION:
+ printVersion();
+ break;
+
+ case ACTION_QUIT:
+ break;
+
+ case ACTION_USAGE: // fall through
+ default:
+ printUsage();
+ break;
+
+ }
+
+ return error ? 1 : 0;
+}
diff --git a/doxygen2qthelp.pro b/doxygen2qthelp.pro
new file mode 100644
index 0000000..495f9fb
--- /dev/null
+++ b/doxygen2qthelp.pro
@@ -0,0 +1,75 @@
+message(Using Qt sources from $(QTSRCDIR))
+ASSISTANT_LIB_DIR = "$(QTSRCDIR)/tools/assistant/lib"
+
+QBASE_LOCATION = "$(QTSRCDIR)/src/qbase.pri"
+include($${QBASE_LOCATION}) | error(File $${QBASE_LOCATION} not found)
+
+TARGET = doxygen2qthelp
+TEMPLATE = app
+DESTDIR = $$PWD
+QT += sql xml
+DEFINES += QHELP_LIB
+# DEFINES += HELP_LIB_DATA_DEBUG
+CONFIG += qt warn_on console
+CONFIG += release
+
+QMAKE_TARGET_PRODUCT = doxygen2qthelp
+QMAKE_TARGET_DESCRIPTION = Doxygen to Qt Help command line converter.
+DEFINES -= QT_ASCII_CAST_WARNINGS
+
+RESOURCES += $${ASSISTANT_LIB_DIR}/helpsystem.qrc
+
+SOURCES += \
+ $${ASSISTANT_LIB_DIR}/qhelpenginecore.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpengine.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpdbreader.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpcontentwidget.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpindexwidget.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpgenerator.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpdatainterface.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpprojectdata.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpcollectionhandler.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchengine.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchquerywidget.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchresultwidget.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchindex_default.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchindexwriter_default.cpp \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchindexreader_default.cpp \
+
+HEADERS += \
+ $${ASSISTANT_LIB_DIR}/qhelpenginecore.h \
+ $${ASSISTANT_LIB_DIR}/qhelpengine.h \
+ $${ASSISTANT_LIB_DIR}/qhelpengine_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelp_global.h \
+ $${ASSISTANT_LIB_DIR}/qhelpdbreader_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpcontentwidget.h \
+ $${ASSISTANT_LIB_DIR}/qhelpindexwidget.h \
+ $${ASSISTANT_LIB_DIR}/qhelpgenerator_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpdatainterface_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpprojectdata_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpcollectionhandler_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchengine.h \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchquerywidget.h \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchresultwidget.h \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchindex_default_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchindexwriter_default_p.h \
+ $${ASSISTANT_LIB_DIR}/qhelpsearchindexreader_default_p.h \
+
+INCLUDEPATH += $${ASSISTANT_LIB_DIR}
+
+SOURCES += \
+ htmlhelpdatainterface.cpp \
+ htmlhelpparser.cpp \
+ qthelpprojectwriter.cpp \
+ simplehtmlparser.cpp \
+ \
+ doxygen2qthelp.cpp \
+
+HEADERS += \
+ htmlhelpdatainterface_p.h \
+ htmlhelpparser_p.h \
+ qthelpprojectwriter_p.h \
+ simplehtmlparser_p.h \
+ \
+ simplehtmlreceiver_p.h \
+
diff --git a/examples/cpptest.ini b/examples/cpptest.ini
new file mode 100644
index 0000000..fc6ec60
--- /dev/null
+++ b/examples/cpptest.ini
@@ -0,0 +1,5 @@
+ForceOverwrite=1
+Namespace=net.sourceforge.cpptest
+Verbose=1
+VirtualFolder=cpptest-1.1.0
+
diff --git a/examples/libspiff.ini b/examples/libspiff.ini
new file mode 100644
index 0000000..cde73ae
--- /dev/null
+++ b/examples/libspiff.ini
@@ -0,0 +1,5 @@
+ForceOverwrite=1
+Namespace=net.sourceforge.libspiff
+Verbose=1
+VirtualFolder=libspiff-0.8.3
+
diff --git a/examples/make_docs_cpptest.sh b/examples/make_docs_cpptest.sh
new file mode 100644
index 0000000..9823560
--- /dev/null
+++ b/examples/make_docs_cpptest.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env bash
+source make_docs_helper.sh
+go cpptest 1.1.0 .tar.gz
+
diff --git a/examples/make_docs_helper.sh b/examples/make_docs_helper.sh
new file mode 100644
index 0000000..ec5b0d4
--- /dev/null
+++ b/examples/make_docs_helper.sh
@@ -0,0 +1,58 @@
+function go() {
+
+PACKAGE_NAME=$1
+PACKAGE_VERSION=$2
+ARCHIVE_EXT=$3
+
+URI_PREFIX="http://downloads.sourceforge.net/${PACKAGE_NAME}/"
+URI_BASE="${PACKAGE_NAME}-${PACKAGE_VERSION}"
+URI_FILE="${URI_BASE}${ARCHIVE_EXT}"
+INI_FILE="${PACKAGE_NAME}.ini"
+QCH_FILE="${PACKAGE_NAME}-${PACKAGE_VERSION}-doc.qch"
+QHP_FILE="${PACKAGE_NAME}-${PACKAGE_VERSION}-doc.qhp"
+
+HPP_FILE="html/index.hhp"
+HPP_LOCATION="${URI_BASE}/doc/${HPP_FILE}"
+
+## Download
+if [ ! -f "${URI_FILE}" ]; then
+ wget "${URI_PREFIX}${URI_FILE}" || exit 1
+fi
+
+## Unpack
+if [ ! -d "${URI_BASE}" ]; then
+ tar xf "${URI_FILE}"
+fi
+
+pushd "${URI_BASE}/doc" || exit 1
+
+## Make temp file
+TEMPFILE=`mktemp`
+if [ "x${TEMPFILE}" = x ]; then
+ popd ; exit 1
+fi
+
+## Fix Doxygen input
+sed -e 's/^\(GENERATE_HTMLHELP\).\+$/\1 = YES/' -e 's/^\(HHC_LOCATION.\+\)$/# \1/' Doxyfile > "${TEMPFILE}"
+rm Doxyfile
+mv "${TEMPFILE}" Doxyfile || { popd ; exit 1 ; }
+
+## Create Doxygen output
+if [ ! -f "${HPP_FILE}" ]; then
+ doxygen || { popd ; exit 1 ; }
+fi
+
+popd || exit 1
+
+
+## Create Qt help files
+LD_LIBRARY_PATH=$QTDIR/lib ../doxygen2qthelp "--config=${INI_FILE}" \
+ --qch "${HPP_LOCATION}" "${QCH_FILE}" || exit 1
+
+LD_LIBRARY_PATH=$QTDIR/lib ../doxygen2qthelp "--config=${INI_FILE}" \
+ --qhp "${HPP_LOCATION}" "${QHP_FILE}" || exit 1
+
+exit 0
+
+}
+
diff --git a/examples/make_docs_libspiff.sh b/examples/make_docs_libspiff.sh
new file mode 100644
index 0000000..7151fec
--- /dev/null
+++ b/examples/make_docs_libspiff.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env bash
+source make_docs_helper.sh
+go libspiff 0.8.3 .tar.bz2
+
diff --git a/examples/make_docs_qwt.sh b/examples/make_docs_qwt.sh
new file mode 100644
index 0000000..f4e66aa
--- /dev/null
+++ b/examples/make_docs_qwt.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env bash
+source make_docs_helper.sh
+go qwt 5.0.2 .tar.bz2
+
diff --git a/examples/qwt.ini b/examples/qwt.ini
new file mode 100644
index 0000000..af6ed08
--- /dev/null
+++ b/examples/qwt.ini
@@ -0,0 +1,5 @@
+ForceOverwrite=1
+Namespace=net.sourceforge.qwt
+Verbose=1
+VirtualFolder=qwt-5.0.2
+
diff --git a/htmlhelpdatainterface.cpp b/htmlhelpdatainterface.cpp
new file mode 100644
index 0000000..5a2fe58
--- /dev/null
+++ b/htmlhelpdatainterface.cpp
@@ -0,0 +1,191 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "htmlhelpdatainterface_p.h"
+#include "htmlhelpparser_p.h"
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QVariant>
+#include <QtCore/QSettings>
+
+QT_BEGIN_NAMESPACE
+
+HtmlHelpDataInterface::HtmlHelpDataInterface(const QString &projectFileName,
+ const QString &namespaceName, const QString &virtualFolder)
+ : m_nameSpace(namespaceName), m_virtualFolder(virtualFolder)
+{
+ initFromDoxygenHtmlHelpProject(projectFileName);
+}
+
+void HtmlHelpDataInterface::initFromDoxygenHtmlHelpProject(
+ const QString &projectFileName)
+{
+ // Parse hhc/hhk/hhp
+ QSettings iniReader(projectFileName, QSettings::IniFormat);
+ m_projectTitle = iniReader.value("OPTIONS/Title").toString();
+ m_filterIdent = m_projectTitle;
+ m_entryPageHref = iniReader.value("OPTIONS/Default topic",
+ "index.html").toString();
+ parseToc(getTocFileName(projectFileName));
+ parseIndex(getIndexFileName(projectFileName));
+
+ // File list
+ m_rootPath = getRootPath(projectFileName);
+ scanRootPath();
+
+ // Custom filters
+ QHelpDataCustomFilter everything;
+ everything.filterAttributes << m_filterIdent;
+ everything.name = m_projectTitle;
+ m_custumFilters << everything;
+
+ // Sections
+ QHelpDataFilterSection section;
+ section.addFilterAttribute(m_filterIdent);
+ section.setIndices(m_index);
+ section.setContents(m_toc);
+ section.setFiles(m_files);
+ m_filterSections.append(section);
+}
+
+const QStringList HtmlHelpDataInterface::applyPrefix(const QStringList &list,
+ const QString &prefix) const
+{
+ if (prefix.isNull()) {
+ return list;
+ }
+
+ QStringList processed;
+ foreach (const QString &entry, list) {
+ processed << prefix + "/" + entry;
+ }
+ return processed;
+}
+
+QStringList HtmlHelpDataInterface::findSubDirs(const QString &dir) const
+{
+ return QDir(dir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+}
+
+QStringList HtmlHelpDataInterface::findDoxygenFiles(const QString &dir) const
+{
+ const QStringList filter = QStringList()
+ << "*.css"
+ << "*.gif"
+ << "*.htm"
+ << "*.html"
+ << "*.jpeg"
+ << "*.jpg"
+ << "*.png"
+ ;
+ return QDir(dir).entryList(filter);
+}
+
+QStringList HtmlHelpDataInterface::findDoxygenFilesRecursively(const QString &dir,
+ const QString &prefix) const
+{
+ QStringList files;
+ foreach (const QString &subDir, findSubDirs(dir)) {
+ files += findDoxygenFilesRecursively(dir + "/" + subDir,
+ prefix.isNull() ? subDir : prefix + "/" + subDir);
+ }
+ return files + applyPrefix(findDoxygenFiles(dir), prefix);
+}
+
+void HtmlHelpDataInterface::scanRootPath()
+{
+ m_files = findDoxygenFilesRecursively(m_rootPath);
+}
+
+void HtmlHelpDataInterface::parseToc(const QString &fileName)
+{
+ HtmlHelpParser parser;
+ parser.parseFile(fileName);
+ m_toc = parser.stealToc(m_projectTitle, m_entryPageHref);
+}
+
+void HtmlHelpDataInterface::parseIndex(const QString &fileName)
+{
+ HtmlHelpParser parser;
+ parser.parseFile(fileName);
+ m_index = parser.stealIndex();
+}
+
+QString HtmlHelpDataInterface::replaceFileExtension(const QString &fileName,
+ const QString &extension)
+{
+ QFileInfo fileInfo(fileName);
+ const int suffixLen = fileInfo.suffix().count();
+ return fileName.left(fileName.count() - suffixLen) + extension;
+}
+
+QString HtmlHelpDataInterface::getTocFileName(const QString &projectFileName)
+{
+ return replaceFileExtension(projectFileName, QLatin1String("hhc"));
+}
+
+QString HtmlHelpDataInterface::getIndexFileName(const QString &projectFileName)
+{
+ return replaceFileExtension(projectFileName, QLatin1String("hhk"));
+}
+
+QString HtmlHelpDataInterface::getRootPath(const QString &projectFileName)
+{
+ QFileInfo fileInfo(projectFileName);
+ return fileInfo.dir().absolutePath();
+}
+
+QString HtmlHelpDataInterface::namespaceName() const
+{
+ return m_nameSpace;
+}
+
+QString HtmlHelpDataInterface::virtualFolder() const
+{
+ return m_virtualFolder;
+}
+
+QList<QHelpDataCustomFilter> HtmlHelpDataInterface::customFilters() const
+{
+ return m_custumFilters;
+}
+
+QList<QHelpDataFilterSection> HtmlHelpDataInterface::filterSections() const
+{
+ return m_filterSections;
+}
+
+QMap<QString, QVariant> HtmlHelpDataInterface::metaData() const
+{
+ return QMap<QString, QVariant>();
+}
+
+QString HtmlHelpDataInterface::rootPath() const
+{
+ return m_rootPath;
+}
+
+QT_END_NAMESPACE
diff --git a/htmlhelpdatainterface_p.h b/htmlhelpdatainterface_p.h
new file mode 100644
index 0000000..1efd2e1
--- /dev/null
+++ b/htmlhelpdatainterface_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#ifndef HTMLHELPDATAINTERFACE_P_H
+#define HTMLHELPDATAINTERFACE_P_H
+
+#include "qhelpdatainterface_p.h"
+#include <QString>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+
+class HtmlHelpParser;
+
+class HtmlHelpDataInterface : public QHelpDataInterface
+{
+private:
+ QString m_projectTitle;
+ QString m_filterIdent;
+ QString m_entryPageHref;
+ QList<QHelpDataIndexItem> m_index;
+ QList<QHelpDataContentItem *> m_toc;
+
+ QString m_rootPath;
+ QString m_nameSpace;
+ QString m_virtualFolder;
+ QStringList m_files;
+ QList<QHelpDataCustomFilter> m_custumFilters;
+ QList<QHelpDataFilterSection> m_filterSections;
+
+public:
+ HtmlHelpDataInterface(const QString &projectFileName,
+ const QString &namespaceName, const QString &virtualFolder);
+
+private:
+ void initFromDoxygenHtmlHelpProject(const QString &projectFileName);
+
+ const QStringList applyPrefix(const QStringList &list, const QString &prefix) const;
+ QStringList findSubDirs(const QString &dir) const;
+ QStringList findDoxygenFiles(const QString &dir) const;
+ QStringList findDoxygenFilesRecursively(const QString &dir,
+ const QString &prefix = QString()) const;
+ void scanRootPath();
+ void parseToc(const QString &fileName);
+ void parseIndex(const QString &fileName);
+
+ QString replaceFileExtension(const QString &fileName,
+ const QString &extension);
+ QString getTocFileName(const QString &projectFileName);
+ QString getIndexFileName(const QString &projectFileName);
+ QString getRootPath(const QString &projectFileName);
+
+private:
+ QString namespaceName() const;
+ QString virtualFolder() const;
+ QList<QHelpDataCustomFilter> customFilters() const;
+ QList<QHelpDataFilterSection> filterSections() const;
+ QMap<QString, QVariant> metaData() const;
+ QString rootPath() const;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // HTMLHELPDATAINTERFACE_P_H
+
diff --git a/htmlhelpparser.cpp b/htmlhelpparser.cpp
new file mode 100644
index 0000000..3298568
--- /dev/null
+++ b/htmlhelpparser.cpp
@@ -0,0 +1,476 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "htmlhelpparser_p.h"
+#include "simplehtmlparser_p.h"
+#include "qhelpdatainterface_p.h"
+
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QFile>
+#include <QtCore/QHash>
+#include <QtCore/QSet>
+
+#ifdef HELP_LIB_DATA_DEBUG
+# include <QDebug>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class ContainerNode;
+class LinkNode;
+
+class HtmlHelpNode
+{
+public:
+ virtual ~HtmlHelpNode() { }
+
+ virtual bool isContainer() const = 0;
+ virtual ContainerNode * toContainer() const = 0;
+ virtual const LinkNode * toLink() const = 0;
+
+#ifdef HELP_LIB_DATA_DEBUG
+ virtual void dump(int level) const = 0;
+#endif
+};
+
+class ContainerNode : public HtmlHelpNode
+{
+public:
+ QList<HtmlHelpNode *> m_children;
+
+ ~ContainerNode()
+ {
+ foreach(const HtmlHelpNode * node, m_children) {
+ delete node;
+ }
+ }
+
+ static void insertItem(QHelpDataIndexItem &item, QList<QHelpDataIndexItem> &dest,
+ QSet<QHelpDataIndexItem> &pastEntries, QSet<QString> &pastIds);
+
+ QHelpDataContentItem * convertAndStealChildren(
+ QHelpDataContentItem * parent,
+ const QString &name = QString(), const QString &href = QString());
+ QList<QHelpDataIndexItem> convertAndStealChildren(int level,
+ const QString &rootLabel, QSet<QHelpDataIndexItem> &pastEntries,
+ QSet<QString> &pastIds);
+
+ bool isContainer() const { return true; }
+ ContainerNode * toContainer() const
+ {
+ return const_cast<ContainerNode *>(this);
+ }
+ const LinkNode * toLink() const { return NULL; }
+
+#ifdef HELP_LIB_DATA_DEBUG
+ void dump(int level = 0) const
+ {
+ QString indent;
+ for (int i = 0; i < level; i++) {
+ indent.append(" _");
+ }
+ foreach(const HtmlHelpNode * node, m_children) {
+ Q_ASSERT(node != NULL);
+ node->dump(level + 1);
+ }
+ }
+#endif
+};
+
+class LinkNode : public HtmlHelpNode
+{
+public:
+ QString m_name;
+ QString m_subName;
+ QString m_href;
+
+ LinkNode(const QString &name, const QString &subName, const QString &href)
+ : m_name(name), m_subName(subName), m_href(href) { }
+
+ QHelpDataContentItem * convert(QHelpDataContentItem * parent) const;
+ QHelpDataIndexItem convert() const;
+ bool isContainer() const { return false; }
+ ContainerNode * toContainer() const { return NULL; }
+ const LinkNode * toLink() const { return this; }
+
+#ifdef HELP_LIB_DATA_DEBUG
+ void dump(int level = 0) const
+ {
+ QString indent;
+ for (int i = 0; i < level; i++) {
+ indent.append(" _");
+ }
+ qDebug() << indent << m_name << m_subName << m_href;
+ }
+#endif
+};
+
+QHelpDataContentItem * ContainerNode::convertAndStealChildren(
+ QHelpDataContentItem * parent, const QString &name,
+ const QString &href)
+{
+ QHelpDataContentItem * const res
+ = new QHelpDataContentItem(parent, name, href);
+
+ const int childCount = m_children.count();
+ for (int i = 0; i < childCount; i++) {
+ QHelpDataContentItem * item = NULL;
+ HtmlHelpNode * const node = m_children[i];
+ Q_ASSERT(node != NULL);
+ if (node->isContainer()) {
+ // No Name node before,
+ item = node->toContainer()->convertAndStealChildren(res);
+ } else {
+ const LinkNode * const linkNode = node->toLink();
+ if ((i < childCount - 1) && (m_children[i + 1]->isContainer())) {
+ // Not last node, check successor
+ HtmlHelpNode * const succ = m_children[i + 1];
+ item = succ->toContainer()->convertAndStealChildren(
+ res, linkNode->m_name, linkNode->m_href);
+ delete succ;
+ // Skip container node
+ i++;
+ } else {
+ // Successor (if existing) is a link node too
+ // Delete link to the 'main page' so we don't have two of them
+ if ((parent != NULL) || (i != 0)
+ || (linkNode->m_href != href)) {
+ if (!linkNode->m_href.isEmpty()) {
+ item = linkNode->convert(res);
+ }
+ }
+ }
+ }
+
+ delete node;
+ }
+ m_children.clear();
+
+ return res;
+}
+
+static uint qHash(const QHelpDataIndexItem &key)
+{
+ return qHash(key.name) ^ qHash(key.reference);
+}
+
+/*static*/ void ContainerNode::insertItem(QHelpDataIndexItem &item,
+ QList<QHelpDataIndexItem> &dest, QSet<QHelpDataIndexItem> &pastEntries,
+ QSet<QString> &pastIds)
+{
+ const QString &id = item.identifier;
+ const bool nameRefPairFound = pastEntries.contains(item);
+ const bool idFound = pastIds.contains(id);
+ if (!(nameRefPairFound && idFound)) {
+ if (nameRefPairFound) {
+ if (idFound) {
+ // (1) Name with exact same link already there
+ // (2) ID already taken
+ // \--> don't add
+ Q_ASSERT(false);
+ } else {
+ // (1) Name with exact same link already there
+ // (2) ID not taken
+ // \--> add as ("", id, ref)
+ item.name = QString();
+ }
+ } else {
+ if (idFound) {
+ // (1) Name not linking to this reference yet
+ // (2) ID already taken
+ // \--> add as (name, "", ref)
+ item.identifier = QString();
+ } else {
+ // (1) Name not linking to this reference yet
+ // (2) ID not taken
+ // \--> add unmodified
+ }
+ }
+#ifdef HELP_LIB_DATA_DEBUG
+ item.dump();
+#endif
+ pastEntries.insert(item);
+ pastIds.insert(id);
+ dest << item;
+ }
+}
+
+QList<QHelpDataIndexItem> ContainerNode::convertAndStealChildren(
+ int level, const QString &rootLabel,
+ QSet<QHelpDataIndexItem> &pastEntries, QSet<QString> &pastIds)
+{
+ QList<QHelpDataIndexItem> res;
+ QString workLabel(rootLabel);
+
+ foreach (HtmlHelpNode * node, m_children) {
+ if (node->isContainer()) {
+ res += node->toContainer()->convertAndStealChildren(
+ level + 1, workLabel, pastEntries, pastIds);
+ } else {
+ QHelpDataIndexItem item = node->toLink()->convert();
+ insertItem(item, res, pastEntries, pastIds);
+ if (level == 0) {
+ workLabel = item.name;
+ } else {
+ // -- Sample input index tree
+ // clone {#1, #2}
+ // * Chicken {#3}
+ // * Salad {#4}
+ //
+ // Chicken {#3}
+ // * clone {#1}
+ //
+ // Salad {#4}
+ // * clone {#2}
+ //
+ // -- Created index entries
+ // NAME | ID | REF
+ // --------+----------------+--------
+ // clone | clone | 1
+ // clone | | 2
+ // Chicken | Chicken | 3
+ // | clone::Chicken | 3
+ // | Chicken::clone | 1
+ // Salad | Salad | 4
+ // | clone::Salad | 4
+ // | Salad::clone | 2
+ const QString id = rootLabel + QLatin1String("::") + item.name;
+ QHelpDataIndexItem extraItem = QHelpDataIndexItem(QString(), id, item.reference);
+ insertItem(extraItem, res, pastEntries, pastIds);
+ }
+ }
+ delete node;
+ }
+
+ m_children.clear();
+ return res;
+}
+
+QHelpDataContentItem * LinkNode::convert(QHelpDataContentItem * parent) const
+{
+ return new QHelpDataContentItem(parent, m_name, m_href);
+}
+
+QHelpDataIndexItem LinkNode::convert() const
+{
+ return QHelpDataIndexItem(m_name, m_name, m_href);
+}
+
+HtmlHelpParser::HtmlHelpParser()
+ : m_state(WAIT_NAME_OR_LOCAL),
+ m_tree(new ContainerNode)
+{
+ m_nodeStack.push(m_tree);
+}
+
+HtmlHelpParser::~HtmlHelpParser()
+{
+ delete m_tree;
+}
+
+void HtmlHelpParser::parseFile(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return;
+ }
+
+ QString content;
+ while (!file.atEnd()) {
+ content.append(file.readLine());
+ }
+ file.close();
+
+ parseString(content);
+}
+
+void HtmlHelpParser::parseString(const QString &text)
+{
+ SimpleHtmlParser parser;
+ parser.parse(text, this);
+}
+
+const QList<QHelpDataIndexItem> HtmlHelpParser::stealIndex()
+{
+ QSet<QHelpDataIndexItem> noDupEntries;
+ QSet<QString> noDupIds;
+ return m_tree->convertAndStealChildren(0, QString(),
+ noDupEntries, noDupIds);
+}
+
+const QList<QHelpDataContentItem*> HtmlHelpParser::stealToc(
+ const QString &rootName, const QString &rootHref)
+{
+ Q_ASSERT(m_tree != NULL);
+ QHelpDataContentItem * const convertedTree
+ = m_tree->convertAndStealChildren(NULL, rootName, rootHref);
+ Q_ASSERT(convertedTree != NULL);
+#ifdef HELP_LIB_DATA_DEBUG
+ convertedTree->dump();
+#endif
+ QList<QHelpDataContentItem*> res;
+ res.append(convertedTree);
+ return res;
+}
+
+void HtmlHelpParser::onTagOpen(const QString &tagName,
+ const QStringList &attributes)
+{
+#ifdef HELP_LIB_DATA_DEBUG
+ qDebug() << "OPEN" << tagName << "ATTS" << attributes;
+#endif
+ if (tagName == QString("ul")) {
+ ContainerNode * const top = m_nodeStack.top();
+ Q_ASSERT(top != NULL);
+ if (!((top == m_tree) && (m_tree->m_children.isEmpty()))) {
+ ContainerNode * const node = new ContainerNode;
+ Q_ASSERT(node != NULL);
+ top->m_children.append(node);
+ m_nodeStack.push(node);
+ }
+ } else if (tagName == QString("param")) {
+ const QString name = findAttribute(attributes, "name");
+ if (name.isEmpty()) {
+ return;
+ }
+ const QString value = findAttribute(attributes, "value");
+ if (value.isEmpty()) {
+ return;
+ }
+
+ if (name == QString("Name")) {
+ switch (m_state) {
+ case WAIT_NAME_OR_LOCAL:
+ // First entry is a name, nothing fixed yet
+ m_name = value;
+ m_state = WAIT_NAME_NAME_OR_NAME_LOCAL;
+ break;
+
+ case WAIT_NAME_NAME_OR_NAME_LOCAL:
+ // Second entry is a name too -> must be a list
+ m_subName = value;
+ m_state = WAIT_NAME_NAME_LOCAL;
+ break;
+
+ case WAIT_NAME_NAME_LOCAL_NAME:
+ m_subName = value;
+ m_state = WAIT_NAME_NAME_LOCAL;
+ break;
+
+ case WAIT_LOCAL_NAME:
+ { ContainerNode * const top = m_nodeStack.top();
+ Q_ASSERT(top != NULL);
+ top->m_children.append(new LinkNode(value, "", m_href)); }
+ m_state = WAIT_NAME_OR_LOCAL;
+ break;
+
+ default:
+ ; // NOOP
+ }
+ } else if (name == QString("Local")) {
+ switch (m_state) {
+ case WAIT_NAME_OR_LOCAL:
+ // First entry is a local -> must be a single link, name follows
+ m_href = value;
+ m_state = WAIT_LOCAL_NAME;
+ break;
+
+ case WAIT_NAME_NAME_OR_NAME_LOCAL:
+ // Second entry is a href -> must be a single link
+ { ContainerNode * const top = m_nodeStack.top();
+ Q_ASSERT(top != NULL);
+ top->m_children.append(new LinkNode(m_name, "", value)); }
+
+ m_state = WAIT_NAME_OR_LOCAL;
+ break;
+
+ case WAIT_NAME_NAME_LOCAL:
+ { ContainerNode * const top = m_nodeStack.top();
+ Q_ASSERT(top != NULL);
+ top->m_children.append(new LinkNode(m_name, m_subName, value)); }
+
+ m_state = WAIT_NAME_NAME_LOCAL_NAME;
+ break;
+
+ default:
+ ; // NOOP
+ }
+ }
+ }
+}
+
+void HtmlHelpParser::onTagClose(const QString &tagName)
+{
+#ifdef HELP_LIB_DATA_DEBUG
+ qDebug() << "CLOSE" << tagName;
+#endif
+ if (tagName == QString("ul")) {
+ if (m_nodeStack.size() > 1) {
+ m_nodeStack.pop();
+ }
+ } else if (tagName == QString("object")) {
+ switch (m_state) {
+ case WAIT_NAME_NAME_OR_NAME_LOCAL:
+ // Name only, no href
+ { ContainerNode * const top = m_nodeStack.top();
+ Q_ASSERT(top != NULL);
+ top->m_children.append(new LinkNode(m_name, "", "")); }
+ break;
+
+ default:
+ ; // NOOP
+ }
+
+ m_state = WAIT_NAME_OR_LOCAL;
+ m_name.clear();
+ m_subName.clear();
+ }
+}
+
+void HtmlHelpParser::onTextChunk(const QString & /*text*/)
+{
+ // NOOP
+}
+
+void HtmlHelpParser::onStop()
+{
+ m_nodeStack.clear();
+#ifdef HELP_LIB_DATA_DEBUG
+ m_tree->dump();
+#endif
+}
+
+/*static*/ QString HtmlHelpParser::findAttribute(const QStringList &list,
+ const QString &key)
+{
+ const int count = list.count();
+ for (int i = 0; i < count - 1; i += 2) {
+ if (list.at(i) == key) {
+ return list.at(i + 1);
+ }
+ }
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/htmlhelpparser_p.h b/htmlhelpparser_p.h
new file mode 100644
index 0000000..277147f
--- /dev/null
+++ b/htmlhelpparser_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#ifndef HTMLHELPPARSER_P_H
+#define HTMLHELPPARSER_P_H
+
+#include "simplehtmlreceiver_p.h"
+#include <QString>
+#include <QStack>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class ContainerNode;
+struct QHelpDataIndexItem;
+class QHelpDataContentItem;
+
+
+class HtmlHelpParser : public SimpleHtmlReceiver
+{
+private:
+ enum State {
+ WAIT_NAME_OR_LOCAL,
+ WAIT_NAME_NAME_OR_NAME_LOCAL,
+ WAIT_LOCAL_NAME,
+ WAIT_NAME_NAME_LOCAL_NAME,
+ WAIT_NAME_NAME_LOCAL
+ };
+
+public:
+ HtmlHelpParser();
+ ~HtmlHelpParser();
+
+ void parseFile(const QString &fileName);
+ void parseString(const QString &text);
+ const QList<QHelpDataIndexItem> stealIndex();
+ const QList<QHelpDataContentItem*> stealToc(const QString &rootName,
+ const QString &rootHref);
+
+private:
+ void onTagOpen(const QString &tagName, const QStringList &attributes);
+ void onTagClose(const QString &tagName);
+ void onTextChunk(const QString &text);
+ void onStop();
+
+ void finishLastNode();
+
+ static QString findAttribute(const QStringList &list, const QString &key);
+
+ QString m_name;
+ QString m_href;
+ QString m_subName;
+ State m_state;
+ ContainerNode * const m_tree;
+ QStack<ContainerNode *> m_nodeStack;
+};
+
+QT_END_NAMESPACE
+
+#endif // HTMLHELPPARSER_P_H
+
diff --git a/qthelpprojectwriter.cpp b/qthelpprojectwriter.cpp
new file mode 100644
index 0000000..22ea738
--- /dev/null
+++ b/qthelpprojectwriter.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "qthelpprojectwriter_p.h"
+#include <qhelpdatainterface_p.h>
+
+#include <QtCore/QList>
+#include <QtCore/QStringList>
+#include <QtCore/QFile>
+
+QT_BEGIN_NAMESPACE
+
+QtHelpProjectWriter::QtHelpProjectWriter()
+ : m_helpData(NULL) {
+ setAutoFormatting(true);
+}
+
+void QtHelpProjectWriter::writeHead()
+{
+ writeStartDocument();
+ writeStartElement(QLatin1String("QtHelpProject"));
+ writeAttribute(QLatin1String("version"), QLatin1String("1.0"));
+
+ Q_ASSERT(m_helpData != NULL);
+ writeTextElement(QLatin1String("namespace"),
+ m_helpData->namespaceName());
+ writeTextElement(QLatin1String("virtualFolder"),
+ m_helpData->virtualFolder());
+}
+
+void QtHelpProjectWriter::writeCustomFilters()
+{
+ Q_ASSERT(m_helpData != NULL);
+ const QList<QHelpDataCustomFilter> filters = m_helpData->customFilters();
+ foreach (const QHelpDataCustomFilter & filter, filters) {
+ writeStartElement(QLatin1String("customFilter"));
+ writeAttribute(QLatin1String("name"), filter.name);
+ foreach (const QString & att, filter.filterAttributes) {
+ writeTextElement(QLatin1String("filterAttribute"), att);
+ }
+ writeEndElement();
+ }
+}
+
+void QtHelpProjectWriter::writeTocItems(
+ const QList<QHelpDataContentItem*> &items)
+{
+ if (items.isEmpty()) {
+ return;
+ }
+
+ foreach (const QHelpDataContentItem *item, items) {
+ const QList<QHelpDataContentItem*> children = item->children();
+ if (children.isEmpty()) {
+ writeEmptyElement(QLatin1String("section"));
+ } else {
+ writeStartElement(QLatin1String("section"));
+ }
+ writeAttribute(QLatin1String("title"), item->title());
+ writeAttribute(QLatin1String("ref"), item->reference());
+ if (!children.isEmpty()) {
+ writeTocItems(children);
+ writeEndElement();
+ }
+ }
+}
+
+void QtHelpProjectWriter::writeFilterSections()
+{
+ Q_ASSERT(m_helpData != NULL);
+ const QList<QHelpDataFilterSection> sections = m_helpData->filterSections();
+ foreach (const QHelpDataFilterSection & section, sections) {
+ // Filter section
+ writeStartElement(QLatin1String("filterSection"));
+
+ // Filter attributes
+ const QStringList atts = section.filterAttributes();
+ foreach (const QString & att, atts) {
+ writeTextElement(QLatin1String("filterAttribute"), att);
+ }
+
+ // TOC
+ writeStartElement(QLatin1String("toc"));
+ writeTocItems(section.contents());
+ writeEndElement();
+
+ // Keywords
+ const QList<QHelpDataIndexItem> indices = section.indices();
+ writeStartElement(QLatin1String("keywords"));
+ foreach (const QHelpDataIndexItem & indexItem, indices) {
+ const bool nameEmpty = indexItem.name.isEmpty();
+ const bool idEmpty = indexItem.identifier.isEmpty();
+ if (nameEmpty && idEmpty) {
+ continue;
+ }
+
+ writeEmptyElement(QLatin1String("keyword"));
+ if (!nameEmpty) {
+ writeAttribute(QLatin1String("name"), indexItem.name);
+ }
+ if (!idEmpty) {
+ writeAttribute(QLatin1String("id"), indexItem.identifier);
+ }
+ writeAttribute(QLatin1String("ref"), indexItem.reference);
+ }
+ writeEndElement();
+
+ // Files
+ const QStringList files = section.files();
+ writeStartElement(QLatin1String("files"));
+ foreach (const QString & file, files) {
+ writeTextElement(QLatin1String("file"), file);
+ }
+ writeEndElement();
+
+ // Filter section
+ writeEndElement();
+ }
+}
+
+void QtHelpProjectWriter::writeBody()
+{
+ writeCustomFilters();
+ writeFilterSections();
+}
+
+void QtHelpProjectWriter::writeFoot()
+{
+ writeEndElement();
+ writeEndDocument();
+}
+
+bool QtHelpProjectWriter::writeFile(const QHelpDataInterface *helpData,
+ const QString &fileName)
+{
+ Q_ASSERT(helpData != NULL);
+ m_helpData = helpData;
+
+ QFile out(fileName);
+ if (!out.open(QIODevice::WriteOnly))
+ return false;
+ setDevice(&out);
+
+ writeHead();
+ writeBody();
+ writeFoot();
+
+ out.close();
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/qthelpprojectwriter_p.h b/qthelpprojectwriter_p.h
new file mode 100644
index 0000000..0d86f01
--- /dev/null
+++ b/qthelpprojectwriter_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#ifndef QTHELPPROJECTWRITER_H
+#define QTHELPPROJECTWRITER_H
+
+#include <QtXml/QXmlStreamWriter>
+
+QT_BEGIN_NAMESPACE
+
+class QHelpDataInterface;
+class QHelpDataContentItem;
+
+template <class T>
+class QList;
+
+class QtHelpProjectWriter : public QXmlStreamWriter
+{
+private:
+ const QHelpDataInterface *m_helpData;
+
+public:
+ QtHelpProjectWriter();
+ bool writeFile(const QHelpDataInterface *helpData,
+ const QString &fileName);
+
+private:
+ void writeHead();
+ void writeCustomFilters();
+ void writeTocItems(const QList<QHelpDataContentItem*> &items);
+ void writeFilterSections();
+ void writeBody();
+ void writeFoot();
+};
+
+QT_END_NAMESPACE
+
+#endif // QTHELPPROJECTWRITER_H
+
diff --git a/simplehtmlparser.cpp b/simplehtmlparser.cpp
new file mode 100644
index 0000000..579564b
--- /dev/null
+++ b/simplehtmlparser.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "simplehtmlparser_p.h"
+#include "simplehtmlreceiver_p.h"
+
+#include <QtCore/QtAlgorithms>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+
+// see also tst_qtextdocumentfragment.cpp
+#define MAX_ENTITY 258
+static const struct QTextHtmlEntity
+{
+ const char *name;
+ quint16 code;
+} entities[MAX_ENTITY]= {
+ { "AElig", 0x00c6 },
+ { "AMP", 38 },
+ { "Aacute", 0x00c1 },
+ { "Acirc", 0x00c2 },
+ { "Agrave", 0x00c0 },
+ { "Alpha", 0x0391 },
+ { "Aring", 0x00c5 },
+ { "Atilde", 0x00c3 },
+ { "Auml", 0x00c4 },
+ { "Beta", 0x0392 },
+ { "Ccedil", 0x00c7 },
+ { "Chi", 0x03a7 },
+ { "Dagger", 0x2021 },
+ { "Delta", 0x0394 },
+ { "ETH", 0x00d0 },
+ { "Eacute", 0x00c9 },
+ { "Ecirc", 0x00ca },
+ { "Egrave", 0x00c8 },
+ { "Epsilon", 0x0395 },
+ { "Eta", 0x0397 },
+ { "Euml", 0x00cb },
+ { "GT", 62 },
+ { "Gamma", 0x0393 },
+ { "Iacute", 0x00cd },
+ { "Icirc", 0x00ce },
+ { "Igrave", 0x00cc },
+ { "Iota", 0x0399 },
+ { "Iuml", 0x00cf },
+ { "Kappa", 0x039a },
+ { "LT", 60 },
+ { "Lambda", 0x039b },
+ { "Mu", 0x039c },
+ { "Ntilde", 0x00d1 },
+ { "Nu", 0x039d },
+ { "OElig", 0x0152 },
+ { "Oacute", 0x00d3 },
+ { "Ocirc", 0x00d4 },
+ { "Ograve", 0x00d2 },
+ { "Omega", 0x03a9 },
+ { "Omicron", 0x039f },
+ { "Oslash", 0x00d8 },
+ { "Otilde", 0x00d5 },
+ { "Ouml", 0x00d6 },
+ { "Phi", 0x03a6 },
+ { "Pi", 0x03a0 },
+ { "Prime", 0x2033 },
+ { "Psi", 0x03a8 },
+ { "QUOT", 34 },
+ { "Rho", 0x03a1 },
+ { "Scaron", 0x0160 },
+ { "Sigma", 0x03a3 },
+ { "THORN", 0x00de },
+ { "Tau", 0x03a4 },
+ { "Theta", 0x0398 },
+ { "Uacute", 0x00da },
+ { "Ucirc", 0x00db },
+ { "Ugrave", 0x00d9 },
+ { "Upsilon", 0x03a5 },
+ { "Uuml", 0x00dc },
+ { "Xi", 0x039e },
+ { "Yacute", 0x00dd },
+ { "Yuml", 0x0178 },
+ { "Zeta", 0x0396 },
+ { "aacute", 0x00e1 },
+ { "acirc", 0x00e2 },
+ { "acute", 0x00b4 },
+ { "aelig", 0x00e6 },
+ { "agrave", 0x00e0 },
+ { "alefsym", 0x2135 },
+ { "alpha", 0x03b1 },
+ { "amp", 38 },
+ { "and", 0x22a5 },
+ { "ang", 0x2220 },
+ { "apos", 0x0027 },
+ { "aring", 0x00e5 },
+ { "asymp", 0x2248 },
+ { "atilde", 0x00e3 },
+ { "auml", 0x00e4 },
+ { "bdquo", 0x201e },
+ { "beta", 0x03b2 },
+ { "brvbar", 0x00a6 },
+ { "bull", 0x2022 },
+ { "cap", 0x2229 },
+ { "ccedil", 0x00e7 },
+ { "cedil", 0x00b8 },
+ { "cent", 0x00a2 },
+ { "chi", 0x03c7 },
+ { "circ", 0x02c6 },
+ { "clubs", 0x2663 },
+ { "cong", 0x2245 },
+ { "copy", 0x00a9 },
+ { "crarr", 0x21b5 },
+ { "cup", 0x222a },
+ { "curren", 0x00a4 },
+ { "dArr", 0x21d3 },
+ { "dagger", 0x2020 },
+ { "darr", 0x2193 },
+ { "deg", 0x00b0 },
+ { "delta", 0x03b4 },
+ { "diams", 0x2666 },
+ { "divide", 0x00f7 },
+ { "eacute", 0x00e9 },
+ { "ecirc", 0x00ea },
+ { "egrave", 0x00e8 },
+ { "empty", 0x2205 },
+ { "emsp", 0x2003 },
+ { "ensp", 0x2002 },
+ { "epsilon", 0x03b5 },
+ { "equiv", 0x2261 },
+ { "eta", 0x03b7 },
+ { "eth", 0x00f0 },
+ { "euml", 0x00eb },
+ { "euro", 0x20ac },
+ { "exist", 0x2203 },
+ { "fnof", 0x0192 },
+ { "forall", 0x2200 },
+ { "frac12", 0x00bd },
+ { "frac14", 0x00bc },
+ { "frac34", 0x00be },
+ { "frasl", 0x2044 },
+ { "gamma", 0x03b3 },
+ { "ge", 0x2265 },
+ { "gt", 62 },
+ { "hArr", 0x21d4 },
+ { "harr", 0x2194 },
+ { "hearts", 0x2665 },
+ { "hellip", 0x2026 },
+ { "iacute", 0x00ed },
+ { "icirc", 0x00ee },
+ { "iexcl", 0x00a1 },
+ { "igrave", 0x00ec },
+ { "image", 0x2111 },
+ { "infin", 0x221e },
+ { "int", 0x222b },
+ { "iota", 0x03b9 },
+ { "iquest", 0x00bf },
+ { "isin", 0x2208 },
+ { "iuml", 0x00ef },
+ { "kappa", 0x03ba },
+ { "lArr", 0x21d0 },
+ { "lambda", 0x03bb },
+ { "lang", 0x2329 },
+ { "laquo", 0x00ab },
+ { "larr", 0x2190 },
+ { "lceil", 0x2308 },
+ { "ldquo", 0x201c },
+ { "le", 0x2264 },
+ { "lfloor", 0x230a },
+ { "lowast", 0x2217 },
+ { "loz", 0x25ca },
+ { "lrm", 0x200e },
+ { "lsaquo", 0x2039 },
+ { "lsquo", 0x2018 },
+ { "lt", 60 },
+ { "macr", 0x00af },
+ { "mdash", 0x2014 },
+ { "micro", 0x00b5 },
+ { "middot", 0x00b7 },
+ { "minus", 0x2212 },
+ { "mu", 0x03bc },
+ { "nabla", 0x2207 },
+ { "nbsp", 0x00a0 },
+ { "ndash", 0x2013 },
+ { "ne", 0x2260 },
+ { "ni", 0x220b },
+ { "not", 0x00ac },
+ { "notin", 0x2209 },
+ { "nsub", 0x2284 },
+ { "ntilde", 0x00f1 },
+ { "nu", 0x03bd },
+ { "oacute", 0x00f3 },
+ { "ocirc", 0x00f4 },
+ { "oelig", 0x0153 },
+ { "ograve", 0x00f2 },
+ { "oline", 0x203e },
+ { "omega", 0x03c9 },
+ { "omicron", 0x03bf },
+ { "oplus", 0x2295 },
+ { "or", 0x22a6 },
+ { "ordf", 0x00aa },
+ { "ordm", 0x00ba },
+ { "oslash", 0x00f8 },
+ { "otilde", 0x00f5 },
+ { "otimes", 0x2297 },
+ { "ouml", 0x00f6 },
+ { "para", 0x00b6 },
+ { "part", 0x2202 },
+ { "percnt", 0x0025 },
+ { "permil", 0x2030 },
+ { "perp", 0x22a5 },
+ { "phi", 0x03c6 },
+ { "pi", 0x03c0 },
+ { "piv", 0x03d6 },
+ { "plusmn", 0x00b1 },
+ { "pound", 0x00a3 },
+ { "prime", 0x2032 },
+ { "prod", 0x220f },
+ { "prop", 0x221d },
+ { "psi", 0x03c8 },
+ { "quot", 34 },
+ { "rArr", 0x21d2 },
+ { "radic", 0x221a },
+ { "rang", 0x232a },
+ { "raquo", 0x00bb },
+ { "rarr", 0x2192 },
+ { "rceil", 0x2309 },
+ { "rdquo", 0x201d },
+ { "real", 0x211c },
+ { "reg", 0x00ae },
+ { "rfloor", 0x230b },
+ { "rho", 0x03c1 },
+ { "rlm", 0x200f },
+ { "rsaquo", 0x203a },
+ { "rsquo", 0x2019 },
+ { "sbquo", 0x201a },
+ { "scaron", 0x0161 },
+ { "sdot", 0x22c5 },
+ { "sect", 0x00a7 },
+ { "shy", 0x00ad },
+ { "sigma", 0x03c3 },
+ { "sigmaf", 0x03c2 },
+ { "sim", 0x223c },
+ { "spades", 0x2660 },
+ { "sub", 0x2282 },
+ { "sube", 0x2286 },
+ { "sum", 0x2211 },
+ { "sup", 0x2283 },
+ { "sup1", 0x00b9 },
+ { "sup2", 0x00b2 },
+ { "sup3", 0x00b3 },
+ { "supe", 0x2287 },
+ { "szlig", 0x00df },
+ { "tau", 0x03c4 },
+ { "there4", 0x2234 },
+ { "theta", 0x03b8 },
+ { "thetasym", 0x03d1 },
+ { "thinsp", 0x2009 },
+ { "thorn", 0x00fe },
+ { "tilde", 0x02dc },
+ { "times", 0x00d7 },
+ { "trade", 0x2122 },
+ { "uArr", 0x21d1 },
+ { "uacute", 0x00fa },
+ { "uarr", 0x2191 },
+ { "ucirc", 0x00fb },
+ { "ugrave", 0x00f9 },
+ { "uml", 0x00a8 },
+ { "upsih", 0x03d2 },
+ { "upsilon", 0x03c5 },
+ { "uuml", 0x00fc },
+ { "weierp", 0x2118 },
+ { "xi", 0x03be },
+ { "yacute", 0x00fd },
+ { "yen", 0x00a5 },
+ { "yuml", 0x00ff },
+ { "zeta", 0x03b6 },
+ { "zwj", 0x200d },
+ { "zwnj", 0x200c }
+};
+
+static bool operator<(const QString &entityStr, const QTextHtmlEntity &entity)
+{
+ return entityStr < QLatin1String(entity.name);
+}
+
+static bool operator<(const QTextHtmlEntity &entity, const QString &entityStr)
+{
+ return QLatin1String(entity.name) < entityStr;
+}
+
+static QChar resolveEntity(const QString &entity)
+{
+ const QTextHtmlEntity *start = &entities[0];
+ const QTextHtmlEntity *end = &entities[MAX_ENTITY];
+ const QTextHtmlEntity *e = qBinaryFind(start, end, entity);
+ if (e == end)
+ return QChar();
+ return e->code;
+}
+
+static const uint windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
+ 0x20ac, // 0x80
+ 0x0081, // 0x81 direct mapping
+ 0x201a, // 0x82
+ 0x0192, // 0x83
+ 0x201e, // 0x84
+ 0x2026, // 0x85
+ 0x2020, // 0x86
+ 0x2021, // 0x87
+ 0x02C6, // 0x88
+ 0x2030, // 0x89
+ 0x0160, // 0x8A
+ 0x2039, // 0x8B
+ 0x0152, // 0x8C
+ 0x008D, // 0x8D direct mapping
+ 0x017D, // 0x8E
+ 0x008F, // 0x8F directmapping
+ 0x0090, // 0x90 directmapping
+ 0x2018, // 0x91
+ 0x2019, // 0x92
+ 0x201C, // 0x93
+ 0X201D, // 0x94
+ 0x2022, // 0x95
+ 0x2013, // 0x96
+ 0x2014, // 0x97
+ 0x02DC, // 0x98
+ 0x2122, // 0x99
+ 0x0161, // 0x9A
+ 0x203A, // 0x9B
+ 0x0153, // 0x9C
+ 0x009D, // 0x9D direct mapping
+ 0x017E, // 0x9E
+ 0x0178 // 0x9F
+};
+
+void SimpleHtmlParser::parse(const QString &text, SimpleHtmlReceiver *receiver)
+{
+ m_receiver = receiver;
+ txt = text;
+ pos = 0;
+ len = txt.length();
+ parse();
+ Q_ASSERT(m_receiver != NULL);
+ m_receiver->onStop();
+ //dumpHtml();
+}
+
+void SimpleHtmlParser::eatSpace()
+{
+ while (pos < len && txt.at(pos).isSpace() && txt.at(pos)
+ != QChar::ParagraphSeparator) {
+ pos++;
+ }
+}
+
+void SimpleHtmlParser::parse()
+{
+ QString buffer;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('<')) {
+ if (!buffer.isEmpty()) {
+ Q_ASSERT(m_receiver != NULL);
+ m_receiver->onTextChunk(buffer);
+ buffer.clear();
+ }
+ parseTag();
+ } else if (c == QLatin1Char('&')) {
+ buffer += parseEntity();
+ } else {
+ buffer += c;
+ }
+ }
+
+ if (!buffer.isEmpty()) {
+ Q_ASSERT(m_receiver != NULL);
+ m_receiver->onTextChunk(buffer);
+ buffer.clear();
+ }
+}
+
+// parses a tag after "<"
+void SimpleHtmlParser::parseTag()
+{
+ eatSpace();
+
+ // handle comments and other exclamation mark declarations
+ if (hasPrefix(QLatin1Char('!'))) {
+ parseExclamationTag();
+ return;
+ }
+
+ // if close tag just close
+ if (hasPrefix(QLatin1Char('/'))) {
+ parseCloseTag();
+ return;
+ }
+
+ // parse tag name
+ const QString tagName = parseWord().toLower();
+
+ // _need_ at least one space after the tag name,
+ // otherwise there can't be attributes
+ QStringList attributes;
+ if (pos < len && txt.at(pos).isSpace()) {
+ attributes = parseAttributes();
+ }
+
+ Q_ASSERT(m_receiver != NULL);
+ m_receiver->onTagOpen(tagName, attributes);
+
+ // finish tag
+ bool tagClosed = false;
+ while (pos < len && txt.at(pos) != QLatin1Char('>')) {
+ if (txt.at(pos) == QLatin1Char('/'))
+ tagClosed = true;
+
+ pos++;
+ }
+ pos++;
+}
+
+// parses a tag beginning with "/"
+void SimpleHtmlParser::parseCloseTag()
+{
+ ++pos;
+ const QString tagName = parseWord().toLower().trimmed();
+ Q_ASSERT(m_receiver != NULL);
+ m_receiver->onTagClose(tagName);
+
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('>'))
+ break;
+ }
+}
+
+// parses a tag beginning with "!"
+void SimpleHtmlParser::parseExclamationTag()
+{
+ ++pos;
+ if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) {
+ pos += 3;
+ // eat comments
+ int end = txt.indexOf(QLatin1String("-->"), pos);
+ pos = (end >= 0 ? end + 3 : len);
+ } else {
+ // eat internal tags
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('>'))
+ break;
+ }
+ }
+}
+
+// parses an entity after "&", and returns it
+QString SimpleHtmlParser::parseEntity()
+{
+ int recover = pos;
+ QString entity;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c.isSpace() || pos - recover > 9) {
+ goto error;
+ }
+ if (c == QLatin1Char(';'))
+ break;
+ entity += c;
+ }
+ {
+ QChar resolved = resolveEntity(entity);
+ if (!resolved.isNull())
+ return QString(resolved);
+ }
+ if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) {
+ entity.remove(0, 1); // removing leading #
+
+ int base = 10;
+ bool ok = false;
+
+ if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
+ entity.remove(0, 1);
+ base = 16;
+ }
+
+ uint uc = entity.toUInt(&ok, base);
+ if (ok) {
+ if (uc >= 0x80 && uc < 0x80
+ + (sizeof(windowsLatin1ExtendedCharacters)
+ /sizeof(windowsLatin1ExtendedCharacters[0])))
+ uc = windowsLatin1ExtendedCharacters[uc - 0x80];
+ QString str;
+ if (uc > 0xffff) {
+ // surrogate pair
+ uc -= 0x10000;
+ ushort high = uc/0x400 + 0xd800;
+ ushort low = uc%0x400 + 0xdc00;
+ str.append(QChar(high));
+ str.append(QChar(low));
+ } else {
+ str.append(QChar(uc));
+ }
+ return str;
+ }
+ }
+error:
+ pos = recover;
+ return QLatin1String("&");
+}
+
+// parses one word, possibly quoted, and returns it
+QString SimpleHtmlParser::parseWord()
+{
+ QString word;
+ if (hasPrefix(QLatin1Char('\"'))) { // double quotes
+ ++pos;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('\"'))
+ break;
+ else if (c == QLatin1Char('&'))
+ word += parseEntity();
+ else
+ word += c;
+ }
+ } else if (hasPrefix(QLatin1Char('\''))) { // single quotes
+ ++pos;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('\''))
+ break;
+ else
+ word += c;
+ }
+ } else { // normal text
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('>')
+ || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1))
+ || c == QLatin1Char('<')
+ || c == QLatin1Char('=')
+ || c.isSpace()) {
+ --pos;
+ break;
+ }
+ if (c == QLatin1Char('&'))
+ word += parseEntity();
+ else
+ word += c;
+ }
+ }
+ return word;
+}
+
+QStringList SimpleHtmlParser::parseAttributes()
+{
+ QStringList attrs;
+
+ while (pos < len) {
+ eatSpace();
+ if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/')))
+ break;
+ QString key = parseWord().toLower();
+ QString value = QLatin1String("1");
+ if (key.size() == 0)
+ break;
+ eatSpace();
+ if (hasPrefix(QLatin1Char('='))){
+ pos++;
+ eatSpace();
+ value = parseWord();
+ }
+ if (value.size() == 0)
+ continue;
+ attrs << key << value;
+ }
+
+ return attrs;
+}
+
+QT_END_NAMESPACE
diff --git a/simplehtmlparser_p.h b/simplehtmlparser_p.h
new file mode 100644
index 0000000..d3c36ec
--- /dev/null
+++ b/simplehtmlparser_p.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#ifndef SIMPLEHTMLPARSER_H
+#define SIMPLEHTMLPARSER_H
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+class SimpleHtmlReceiver;
+
+class SimpleHtmlParser
+{
+public:
+ void parse(const QString &text, SimpleHtmlReceiver *receiver);
+
+private:
+ void parse();
+ void parseTag();
+ void parseCloseTag();
+ void parseExclamationTag();
+ QString parseEntity();
+ QString parseWord();
+ QStringList parseAttributes();
+ void eatSpace();
+ inline bool hasPrefix(QChar c, int lookahead = 0) const
+ {return pos + lookahead < len && txt.at(pos) == c; }
+
+ SimpleHtmlReceiver *m_receiver;
+ QString txt;
+ int pos;
+ int len;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // SIMPLEHTMLPARSER_H
+
diff --git a/simplehtmlreceiver_p.h b/simplehtmlreceiver_p.h
new file mode 100644
index 0000000..fbc1bd9
--- /dev/null
+++ b/simplehtmlreceiver_p.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the doxygen2qthelp project on Trolltech Labs.
+ **
+ ** This file may be used under the terms of the GNU General Public
+ ** License version 2.0 or 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of
+ ** this file. Please review the following information to ensure GNU
+ ** General Public Licensing requirements will be met:
+ ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you are unsure which license is appropriate for your use, please
+ ** contact the sales department at qt-sales@nokia.com.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#ifndef SIMPLEHTMLRECEIVER_H
+#define SIMPLEHTMLRECEIVER_H
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+class QStringList;
+
+class SimpleHtmlReceiver
+{
+public:
+ virtual ~SimpleHtmlReceiver() { }
+
+ virtual void onTagOpen(const QString &tagName,
+ const QStringList & attributes) = 0;
+ virtual void onTagClose(const QString &tagName) = 0;
+ virtual void onTextChunk(const QString &text) = 0;
+ virtual void onStop() = 0;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // SIMPLEHTMLRECEIVER_H
+